
import requests
import json,re,time,logging
from io import BytesIO
import base64

import psutil


import pyautogui
from pywinauto import Desktop

"""
from pip._internal import main as pipmain
pipmain(['install', 'pyautogui'])
pipmain(['install', 'pillow'])
pipmain(['install', 'psutil'])
pipmain(['install', 'pywinauto'])
"""
from dataclasses import dataclass , asdict ,field
 
@dataclass
class resDate:
    """ 标准通信结构 """
    success: bool = True
    code : int = 0 
    message: str = ""
    status: str = ""
    payload : dict =  field(default_factory=dict) 

def 日志配置(log_file, level=logging.INFO):
    """
    创建一个独立的日志通道，可设置日志级别，并将日志写入文件
    
    :param log_file: 日志文件路径
    :param level:    日志级别(默认为INFO)，可以是 logging.DEBUG/INFO/WARNING/ERROR/CRITICAL
    :return: 配置好的日志器
    """
    # 创建独立的日志器（使用文件路径作为名称保证唯一性）
    logger = logging.getLogger(log_file)
    
    # 设置日志级别（注意：日志器和处理器都要设置）
    logger.setLevel(level)
    
    # 防止重复添加处理器（如果多次调用函数）
    if not logger.handlers:
        # 创建文件处理器
        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(level)  # 处理器级别与日志器一致
        
        console_handler = logging.StreamHandler()
        console_handler.setLevel(level)

        # 设置日志格式
        formatter = logging.Formatter(
            '%(asctime)s - %(levelname)s - %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        file_handler.setFormatter(formatter)
        
        # 添加处理器
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        
    
    return logger
logger = 日志配置("auto-debug.log", level=logging.DEBUG)
logger.info(f'开启日志')

def 最小化所窗口(pid=0):
    """ 跳过指定窗口 """
    windows = Desktop(backend="uia").windows()
    for window in windows:
        wpid = window.process_id()
        if wpid == pid:
            continue
        try:
            #if window.element_info.control_type =='Window':
            window.minimize()  # 最小化每个窗口
        except Exception as e :
            print('异常'+str(e))


def 全屏截图b64(last=None,size=None)->resDate:
    """ 最小化所有窗口 
        截图，编码
        返回 str
    """
    ret = resDate()
    if last is None:
        最小化所窗口()
    try:
        # 截屏 -> 内存二进制 -> Base64
        img_buffer = BytesIO()
        if size :
            pyautogui.screenshot(region=size).save(img_buffer, format="JPEG") 
        else:
            pyautogui.screenshot().save(img_buffer, format="JPEG")
        ret .payload =  base64.b64encode(img_buffer.getvalue()).decode("ascii")
        return ret
    except Exception as e:
        ret.success = False
        ret.message = "截屏失败 可能需要管理员 提示：" + str(e)
        print("截屏失败:", e)
        return ret


def 识别图片b64(b64:str)->resDate:
    """  返回dict  restFul
    
    """
    ret = resDate()
    API_URL = "http://192.168.0.123:8080/ocr"
    payload = {"file": b64, "fileType": 1,"useDocOrientationClassify":False 
               ,"useDocUnwarping":False,"useTextlineOrientation":False}
    try:
        response = requests.post(API_URL, json=payload)
    except Exception as e:
        ret.success= False
        ret.message = '请求访问ocr服务出错==' + str(e) 
        return ret
    if response.status_code != 200:
        ret.success= False
        ret.message = '调用识别服务出错'
        ret.code = response.status_codes
        return ret
    result = response.json()["result"]
    ret.payload = result["ocrResults"]
    logger.info(f'完成ai解析')
    return ret 

def 点击桌面图标(name:str):
    """  通过ocr得到坐标 """
    ret = resDate()
    res = 全屏截图b64()
    #logger.info(f'完成全屏截图')
    if not (res.success ):
        ret.success = False
        ret.message = res.message + "\n 在桌面图标识别 "
        return ret
    ## 寻找文本

    ret = 识别并点击(res.payload,name,"  在桌面图标 没找到 {}  在 {} ",clicks=2) 

 
    return  ret
    
    

def 获取窗口(name:str):
    """ 可以循环 找等待窗口出现 0.5一次
    
    可以用正则表达式  
    """
    ret = resDate()
    #最小化所窗口()
    # 获取所有窗口
    windows = Desktop(backend="uia").windows()  # 或者 backend="win32"
    # 打印所有窗口标题
    titiles = []
    target_window = None
    for window in windows:
        title = window.window_text()
        titiles.append(title)
        #if title == name:  # 过滤无标题窗口
        if re.match(name,title):
            target_window = window
            rect = target_window.rectangle()
            #// 可能是全屏，也可能是无法获取
            if rect.width() == 0 and rect.height() == 0:
                continue
            else:
                break
    
    if target_window  is None:
        ret.success = False
        ret.message = "没有找到 {} 标题符合的窗口 再 {}".format(name,str(titiles))
        return ret
    ret.payload = target_window
    return ret



def 清除默认值(window):
    """ tab 退格键"""
    if not window.is_active():
        window.set_focus() #激活
    for i in range(5):
        pyautogui.press('tab')  
        pyautogui.write(["backspace"] * 16)  #  

 
def 窗口截图b64(target_window,width=None,height=None,last=None)-> resDate:
    """  
    参数  
        窗口 ，  自定义尺寸x,   自定义尺寸y,   是否连续截图
    返回 resDate {  b64, x , y, windwo} 
    
    """
    ret = resDate()
  
    pid = target_window.process_id()
    
    if last is None:
        最小化所窗口(pid)
        time.sleep(0.3)
        target_window.set_focus() #激活
    
    rect = target_window.rectangle()
    title = target_window.window_text()
    print(f"窗口位置: left={rect.left}, top={rect.top}, width={rect.width()}, height={rect.height()}")
    if rect.width() == 0 and rect.height() == 0:
        ret.success = False
        ret.message = '窗口 "{}" 可能最小化了' .format(title)
        return ret
    img_buffer = BytesIO()
    width =  width if  width else rect.width()
    height =  height if  height else rect.height()
    screenshot = pyautogui.screenshot(region= (rect.left, rect.top,width,height))
    screenshot.save(img_buffer, format="JPEG")
    ret.payload = { "b64": base64.b64encode(img_buffer.getvalue()).decode("ascii"),"x":rect.left ,"y": rect.top,"window":target_window}
    return ret


def 查找并填写(name,value,info ,result,winLeft,winTop):
    """ """
    ## 3此点击  （密码）
    ret = resDate()
    nameList = result['rec_texts']
    indices = [i for i, x in enumerate(nameList) if x == name]
    if len(indices) <= 0 :
        ret.success = False
        ret.message = info .format(name,str(nameList) )
        return ret
    nameIndex = indices[0]
    csite =result['rec_polys'][nameIndex]
    cx = int( (csite[0][0]  +  csite[1][0])/2 )
    cy = int ( (csite[0][1]  +  csite[3][1])/2 )
    #pyautogui.moveTo(cx+winLeft, cy+winTop, duration=0.2)
    pyautogui.click(cx+winLeft, cy+winTop, duration=0.2) #加上窗口顶点位置
    #print(cx, cy,winLeft,winTop)
    #time.sleep(0.3)  # 等待点击生效
    pyautogui.write(value,interval=0.01)
    return  ret 

def 自动输入账号密码(name):
    """ 登入
    取消勾选账号密码
    
    
    """
    print("自动登入")
    ret = resDate()
    rect = 获取窗口(name)
    if not rect.success:
        return rect
    window =   rect.payload  
    清除默认值(window)
    time.sleep(0.2)
    response = 窗口截图b64( window )
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    #窗口
    result = 识别图片b64(b64).payload[0]["prunedResult"]
    #查找并填写("保存账号",'',"  没有 保存账号 {}  在 {} ",result,winLeft,winTop)
    查找并填写("密码",'wxy100200300',"  没有找到密码框 {}  在 {} ",result,winLeft,winTop)
    查找并填写("账号",'13884488824',"  没有找到账号框 {}  在 {} ",result,winLeft,winTop)
    查找并填写("登",'',"  没有找到 {}  在 {} ",result,winLeft,winTop)
     
    return  ret

def 识别并点击(b64,name,info ,winLeft=0,winTop=0,clicks=1)->resDate:
    """ 根据图片点击对象  自动识别"""
    ret = resDate()
    #logger.info(f'点击并识别')
    with open(name + "-save.jpg", "wb") as f:
        f.write(base64.b64decode(b64))
    res = 识别图片b64(b64)

    if not res.success :
        return res
    result = res.payload[0]["prunedResult"]
    nameList = result['rec_texts']
    indices = [i for i, x in enumerate(nameList) if x == name]
    logger.info(f'查找文字 "{name}" 位子 {indices}')
    if len(indices) <= 0 :
        ret.success = False
        ret.message = info .format(name,str(nameList) )
        return ret
    nameIndex = indices[0]
    csite =result['rec_polys'][nameIndex]
    cx = int( (csite[0][0]  +  csite[1][0])/2 )
    cy = int ( (csite[0][1]  +  csite[3][1])/2 )
    pyautogui.moveTo(cx+winLeft, cy+winTop, duration=0.2)
    pyautogui.click(cx+winLeft, cy+winTop,clicks=clicks,  duration=0.2) #加上窗口顶点位置
    return ret

def 启动量化功能(name):
    """ 
    打开python子窗口
    
    
    """
    ret = resDate()
    rect = 获取窗口(name)

    if not rect.success:
        return rect
    window =   rect.payload  
    
    
    #////////点击量化
    time.sleep(0.2)
    response = 窗口截图b64( window,600,160 )
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    ret = 识别并点击(b64,"量化",'没有识别到 "{}" 在{}中没找到',winLeft,winTop)
    if not ret.success:
        return ret
    #///// 菜单PythonGO
    time.sleep(0.5)
    response = 窗口截图b64( window,600,160 ,last=1)
   
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    ret = 识别并点击(b64,"PythonGO",'没有识别到 "{}" 在{}中没找到',winLeft,winTop)
    if not ret.success:
        return ret
   
    return  ret


def 子窗口截图b64 (window,title,width=None,height=None,last=None)->resDate:
    
    """  窗口 ，标题  
    ( window,"PythonGO",600,160 ,last=1)
    """
    ret = resDate()
    childs = window.children()
    child_window = None
    for child in childs:
        if title == child.window_text():
            child_window = child
            break
    if child_window is None :
        ret.success = False
        ret.message = f' 没有找到子窗口  "{title}"'
        return ret
    
    return  窗口截图b64(child_window, width,height,last)
    

def 启动量化策略(name)->resDate:
    """  识别子窗口 点击
    
    
    """
    ret = resDate()
    rect = 获取窗口(name)

    if not rect.success:
        return rect
    window =   rect.payload 
    window.set_focus()
    time.sleep(0.2)
    
     
    #// PythonGO点击 策略管理
    response = 子窗口截图b64(window,"PythonGO",200,160)
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    window1 = response.payload["window"]
    print(f'操作窗口 { window1.window_text()  }')
    ret = 识别并点击(b64,"策略管理",'点击 "{}" 在{}中没找到',winLeft,winTop)
    if not ret.success:
        return ret
    time.sleep(0.5)
    #//----策略管理 点击策略
    response = 子窗口截图b64(window1,"策略管理")
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    window2 = response.payload["window"]
    print(f'操作窗口 { window2.window_text()  }')
    ret = 识别并点击(b64,"新的策略中农信",'点击 "{}" 在{}中没找到',winLeft,winTop,clicks=2)
    if not ret.success:
        return ret
    time.sleep(0.5)
    #----  等加载成功  (双击策略)
    for i in  range(5):
        time.sleep(1)
        response = 子窗口截图b64(window1,"策略管理")
        if not response.success:
            continue
        b64 = response.payload["b64"]
        winLeft  = response.payload["x"]
        winTop = response.payload["y"]
        ret = 识别并点击(b64,"双击策略",' 启动策略 "{}" 在{}中没找到',winLeft,winTop,clicks=2)
        print(f'等待加载策略 {i} ')
        if not ret.success:
            continue
        else:
            break
    if not ret.success:
        return ret
    #//  输入实例名
    pyautogui.write("w"+time.strftime("%m%d%H%M"),interval=0.01)
    #//   确定 后启动实例
    response = 子窗口截图b64(window2,"创建实例")
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    window3 = response.payload["window"]
    print(f'操作窗口 { window3.window_text()  }')

    ret = 识别并点击(b64,"确定",' 点击 "{}" 在{}中没找到',winLeft,winTop,clicks=1)
    if not ret.success:
        return ret
    # //  也可以通过alt+f4关闭 子窗口
    time.sleep(0.5)
    pyautogui.hotkey('alt', 'f4')
    # response = 子窗口截图b64(window1,"策略管理")
    # if not response.success:
    #     return response
    # b64 = response.payload["b64"]
    # winLeft  = response.payload["x"]
    # winTop = response.payload["y"]
    # window2 = response.payload["window"]
    # print(f'操作窗口 { window2.window_text()  }')
    # ret = 识别并点击(b64,"×",'点击关闭 "{}" 在{}中没找到',winLeft,winTop,clicks=2)
    # if not ret.success:
    #     return ret
    time.sleep(0.5)
    #//  启动策略
    response = 子窗口截图b64(window,"PythonGO",300,200)
    if not response.success:
        return response
    b64 = response.payload["b64"]
    winLeft  = response.payload["x"]
    winTop = response.payload["y"]
    window1 = response.payload["window"]
    print(f'操作窗口 { window1.window_text()  }')
    ret = 识别并点击(b64,"初始化",'点击 "{}" 在{}中没找到',winLeft,winTop,clicks=2)
    if not ret.success:
        return ret
 


    
    return ret
    
      



      


    






def main ()->resDate:
    """  按步骤来  """
    
    ret = resDate()
    ret = 点击桌面图标('无限易')
    if not ret.success:
        return ret

    for i in range(60*2):
        stat = 获取窗口('^无限易模拟$')
        if  stat.success:
            break
        print(f"等待程序启动 {i}")
        time.sleep(0.5)
    time.sleep(0.5)
    #截图 程序界面
    自动输入账号密码('^无限易模拟$')
    for i in range(60*2):
        stat = 获取窗口('^无限易.*?\\d+$')
        if  stat.success:
            break
        print(f"等待程序加载 {i}")
        time.sleep(0.5)
    time.sleep(0.5)
    启动量化功能('^无限易.*?\\d+$')
    启动量化策略('^无限易.*?\\d+$')
    return ret





if __name__ == "__main__":
    temp =  main() 
    if not temp.success :
        logger.info ( temp.message ) 
